//---------------------------------------------------------------------------
// <copyright file="RibbonContextualTabGroup.cs" company="Microsoft Corporation">
//     Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------

namespace Microsoft.Windows.Controls.Ribbon
{
	#region Using declarations

	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.Windows;
	using System.Windows.Controls;
	using System.Windows.Input;
	using System.Windows.Markup;
	using Microsoft.Windows.Shell;
	using System.Windows.Automation.Peers;

	#endregion

	/// <summary>
	///     Item container for RibbonContextualTabGroupItemsControl
	/// </summary>
	[ContentProperty("Header")]
	public class RibbonContextualTabGroup : Control
	{
		#region Fields

		internal const double TabHeaderSeparatorHeightDelta = 4.0;

		private TabsEnumerable _tabs = null;

		#endregion

		#region Constructors

		/// <summary>
		///   Initializes static members of the RibbonContextualTabGroup class.  Also
		///   overrides the default style.
		/// </summary>
		static RibbonContextualTabGroup()
		{
			DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(typeof(RibbonContextualTabGroup)));
			VisibilityProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(Visibility.Collapsed, new PropertyChangedCallback(OnVisibilityChanged), new CoerceValueCallback(CoerceVisibility)));
			FocusableProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(false));
		}

		#endregion

		#region Public Properties

		/// <summary>
		///     The DependencyProperty for the Header property.
		///     Flags:              None
		///     Default Value:      null
		/// </summary>
		public static readonly DependencyProperty HeaderProperty =
				DependencyProperty.Register(
						"Header",
						typeof(object),
						typeof(RibbonContextualTabGroup),
						new FrameworkPropertyMetadata(
								(object)null,
								new PropertyChangedCallback(OnHeaderChanged)));


		/// <summary>
		///     Header is the data used to for the header of each item in the control.
		/// </summary>
		public object Header
		{
			get { return GetValue(HeaderProperty); }
			set { SetValue(HeaderProperty, value); }
		}

		/// <summary>
		///     Called when HeaderProperty is invalidated on "d."
		/// </summary>
		private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			RibbonContextualTabGroup ctrl = (RibbonContextualTabGroup)d;
			ctrl.OnHeaderChanged(e.OldValue, e.NewValue);
		}

		/// <summary>
		///     This method is invoked when the Header property changes.
		/// </summary>
		/// <param name="oldHeader">The old value of the Header property.</param>
		/// <param name="newHeader">The new value of the Header property.</param>
		protected virtual void OnHeaderChanged(object oldHeader, object newHeader)
		{
			RemoveLogicalChild(oldHeader);
			AddLogicalChild(newHeader);
			UpdateTabs(false /* clear */);
		}

		/// <summary>
		///     The DependencyProperty for the HeaderTemplate property.
		///     Flags:              Can be used in style rules
		///     Default Value:      null
		/// </summary>
		public static readonly DependencyProperty HeaderTemplateProperty =
				DependencyProperty.Register(
						"HeaderTemplate",
						typeof(DataTemplate),
						typeof(RibbonContextualTabGroup),
						new FrameworkPropertyMetadata(
								(DataTemplate)null));

		/// <summary>
		///     HeaderTemplate is the template used to display the <seealso cref="Header"/>.
		/// </summary>
		public DataTemplate HeaderTemplate
		{
			get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
			set { SetValue(HeaderTemplateProperty, value); }
		}


		/// <summary>
		///     The DependencyProperty for the HeaderTemplateSelector property.
		///     Flags:              none
		///     Default Value:      null
		/// </summary>
		public static readonly DependencyProperty HeaderTemplateSelectorProperty =
				DependencyProperty.Register(
						"HeaderTemplateSelector",
						typeof(DataTemplateSelector),
						typeof(RibbonContextualTabGroup),
						new FrameworkPropertyMetadata(
								(DataTemplateSelector)null));

		/// <summary>
		///     HeaderTemplateSelector allows the application writer to provide custom logic
		///     for choosing the template used to display the <seealso cref="Header"/>.
		/// </summary>
		/// <remarks>
		///     This property is ignored if <seealso cref="HeaderTemplate"/> is set.
		/// </remarks>
		public DataTemplateSelector HeaderTemplateSelector
		{
			get { return (DataTemplateSelector)GetValue(HeaderTemplateSelectorProperty); }
			set { SetValue(HeaderTemplateSelectorProperty, value); }
		}

		/// <summary>
		///     The DependencyProperty for the HeaderStringFormat property.
		///     Flags:              None
		///     Default Value:      null
		/// </summary>
		public static readonly DependencyProperty HeaderStringFormatProperty =
				DependencyProperty.Register(
						"HeaderStringFormat",
						typeof(String),
						typeof(RibbonContextualTabGroup),
						new FrameworkPropertyMetadata(
								(String)null));


		/// <summary>
		///     HeaderStringFormat is the format used to display the header content as a string.
		///     This arises only when no template is available.
		/// </summary>
		public String HeaderStringFormat
		{
			get { return (String)GetValue(HeaderStringFormatProperty); }
			set { SetValue(HeaderStringFormatProperty, value); }
		}

		/// <summary>
		///     DependencyProperty for Ribbon property.
		/// </summary>
		public static readonly DependencyProperty RibbonProperty =
			RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonContextualTabGroup));

		/// <summary>
		///     This property is used to access Ribbon
		/// </summary>
		public Ribbon Ribbon
		{
			get { return RibbonControlService.GetRibbon(this); }
		}

		#endregion

		#region Internal Properties

		/// <summary>
		///   Gets or sets a value indicating whether the contextual tab group's label should be shown.
		/// </summary>
		internal bool ShowLabelToolTip
		{
			get { return RibbonHelper.GetIsContentTooltip(VisualChild, Header); }
			set { RibbonHelper.SetContentAsToolTip(this, VisualChild, Header, value); }
		}

		private FrameworkElement VisualChild
		{
			get
			{
				return VisualChildrenCount == 0 ? null : (GetVisualChild(0) as FrameworkElement);
			}
		}

		internal IEnumerable<RibbonTab> Tabs
		{
			get
			{
				if (_tabs == null)
				{
					_tabs = new TabsEnumerable(this);
				}
				return _tabs;
			}
		}

		internal RibbonTab FirstVisibleTab
		{
			get
			{
				IEnumerable<RibbonTab> tabs = Tabs;
				if (tabs != null)
				{
					foreach (RibbonTab tab in tabs)
					{
						if (tab.Visibility == Visibility.Visible)
						{
							return tab;
						}
					}
				}
				return null;
			}
		}

		/// <summary>
		///   Gets or sets the sum of DesiredSize.Width of TabHeaders of this group. 
		/// </summary>
		internal double TabsDesiredWidth
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the width required to display the full Header without trimming. 
		/// </summary>
		internal double IdealDesiredWidth
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets Padding to be added to each TabHeader in this group, 
		/// such that ContextualTabGroupHeader can be displayed in its full length.
		/// Its computed as (IdealDesiredWidth - TabsDesiredWidth)/ (Number of visible Tabs in this group)
		/// </summary>
		internal double DesiredExtraPaddingPerTab
		{
			get;
			set;
		}

		/// <summary>
		///  Gets or sets the arrange width of the contextual tab group.
		/// </summary>
		internal double ArrangeWidth
		{
			get;
			set;
		}

		/// <summary>
		///   Gets or sets the arrange x-coordinate of the contextual tab group.
		/// </summary>
		internal double ArrangeX
		{
			get;
			set;
		}

		#endregion

		#region Protected Methods

		protected override AutomationPeer OnCreateAutomationPeer()
		{
			return new Microsoft.Windows.Automation.Peers.RibbonContextualTabGroupAutomationPeer(this);
		}

		/// <summary>
		///   Callback for mouse down.  Captures/Releases the mouse depending on whether
		///   the tab group was clicked.
		/// </summary>
		/// <param name="e">The event data.</param>
		protected override void OnMouseDown(MouseButtonEventArgs e)
		{
			if (e.ClickCount == 1 || e.ChangedButton == MouseButton.Right)
			{
				this.CaptureMouse();
				if (this.IsMouseCaptured)
				{
					// Though we have already checked this state, our call to CaptureMouse
					// could also end up changing the state, so we check it again.
					if (e.ButtonState != MouseButtonState.Pressed)
					{
						// Release capture since we decided not to press the button.
						this.ReleaseMouseCapture();
					}
				}
				e.Handled = true;
			}
			else if (e.ClickCount == 2 && e.ChangedButton == MouseButton.Left)
			{
				// On DoubleClick maximize/restore the window
				RibbonWindow ribbonWindow = Window.GetWindow(this) as RibbonWindow;
				if (ribbonWindow != null)
				{
					if (SystemCommands.MaximizeWindowCommand.CanExecute(null, ribbonWindow))
					{
						SystemCommands.MaximizeWindowCommand.Execute( /*parameter*/ null, /* target*/ ribbonWindow);
						e.Handled = true;
					}
					else if (SystemCommands.RestoreWindowCommand.CanExecute(null, ribbonWindow))
					{
						SystemCommands.RestoreWindowCommand.Execute(/*parameter*/ null, /* target*/ ribbonWindow);
						e.Handled = true;
					}
				}
			}

			base.OnMouseDown(e);
		}

		/// <summary>
		///   Callback for mouse up, releases mouse capture and sends click notifications.
		/// </summary>
		/// <param name="e">The event data.</param>
		protected override void OnMouseUp(MouseButtonEventArgs e)
		{
			if (this.IsMouseCaptured)
			{
				this.ReleaseMouseCapture();
				if (IsMouseOver)
				{
					if (e.ChangedButton == MouseButton.Left && this.Ribbon != null)
					{
						// Selects the first tab in this contextual group.
						this.Ribbon.NotifyMouseClickedOnContextualTabGroup(this);
						e.Handled = true;
					}
					else if (e.ChangedButton == MouseButton.Right)
					{
						// Show SystemMenu
						RibbonWindow ribbonWindow = Window.GetWindow(this) as RibbonWindow;
						if (ribbonWindow != null)
						{
							if (SystemCommands.ShowSystemMenuCommand.CanExecute(null, ribbonWindow))
							{
								SystemCommands.ShowSystemMenuCommand.Execute( /*parameter*/ null, /* target*/ ribbonWindow);
								e.Handled = true;
							}
						}
					}
				}
			}

			base.OnMouseUp(e);
		}

		#endregion

		#region Private Methods

		internal void PrepareTabGroupHeader(object item,
										DataTemplate itemTemplate,
										DataTemplateSelector itemTemplateSelector,
										string itemStringFormat)
		{
			UpdateTabs(false /* clear */);

			if (item != this)
			{
				// copy styles from the ItemsControl
				if (PropertyHelper.IsDefaultValue(this, HeaderProperty))
				{
					Header = item;
				}
				if (itemTemplate != null)
					SetValue(HeaderTemplateProperty, itemTemplate);
				if (itemTemplateSelector != null)
					SetValue(HeaderTemplateSelectorProperty, itemTemplateSelector);
				if (itemStringFormat != null)
					SetValue(HeaderStringFormatProperty, itemStringFormat);
			}
		}

		internal void ClearTabGroupHeader()
		{
			UpdateTabs(true /* clear */);
		}

		private static object CoerceVisibility(DependencyObject d, object baseValue)
		{
			// Always coerce Hidden to Collapsed
			if ((Visibility)baseValue == Visibility.Hidden)
				return Visibility.Collapsed;
			return baseValue;
		}

		/// <summary>
		///   Callback for Visibility property changed.  
		/// </summary>
		/// <param name="sender">The RibbonContextualTabGroup whose Visibility property changed.</param>
		/// <param name="e">The event data.</param>
		private static void OnVisibilityChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
		{
			((RibbonContextualTabGroup)sender).CoerceTabsVisibility();
		}

		private void UpdateTabs(bool clear)
		{
			if (Ribbon != null)
			{
				for (int i = 0; i < Ribbon.Items.Count; i++)
				{
					RibbonTab tab = Ribbon.ItemContainerGenerator.ContainerFromIndex(i) as RibbonTab;
					if (tab != null && tab.IsContextualTab && Object.Equals(tab.ContextualTabGroupHeader, Header))
					{
						if (!clear)
						{
							tab.ContextualTabGroup = this;
							tab.CoerceValue(VisibilityProperty);
						}
						else
						{
							tab.CoerceValue(VisibilityProperty);
							tab.ContextualTabGroup = null;
						}
					}
				}
			}
		}

		private void CoerceTabsVisibility()
		{
			IEnumerable<RibbonTab> tabs = Tabs;
			if (tabs != null)
			{
				foreach (RibbonTab tab in tabs)
				{
					tab.CoerceValue(VisibilityProperty);
				}
			}
		}

		#endregion

		#region Tabs

		private class TabsEnumerable : IEnumerable<RibbonTab>
		{
			#region Constructor And Properties

			public TabsEnumerable(RibbonContextualTabGroup tabGroup)
			{
				ContextualTabGroup = tabGroup;
			}

			private RibbonContextualTabGroup ContextualTabGroup
			{
				get;
				set;
			}

			#endregion

			#region IEnumerable<RibbonTab> Members

			public IEnumerator<RibbonTab> GetEnumerator()
			{
				Ribbon ribbon = ContextualTabGroup.Ribbon;
				if (ribbon != null)
				{
					int itemCount = ribbon.Items.Count;
					for (int i = 0; i < itemCount; i++)
					{
						RibbonTab tab = ribbon.ItemContainerGenerator.ContainerFromIndex(i) as RibbonTab;
						if (tab != null &&
							tab.IsContextualTab &&
							object.Equals(ContextualTabGroup.Header, tab.ContextualTabGroupHeader))
						{
							yield return tab;
						}
					}
				}
			}

			#endregion

			#region IEnumerable Members

			IEnumerator IEnumerable.GetEnumerator()
			{
				return this.GetEnumerator();
			}

			#endregion
		}

		#endregion
	}
}
